using System;
using System.Diagnostics.Contracts;

namespace OpenCube.Model.Primitives
{
	public struct Coordinates3<T>
		: IEquatable<Coordinates3<T>>
		where T : struct, IEquatable<T>
	{
		public static readonly Coordinates3<T> Empty = new Coordinates3<T>(default(T), default(T), default(T));

		public Coordinates3(T x, T y, T z)
		{
			_x = x;
			_y = y;
			_z = z;
		}

		private readonly T _x;

		/// <summary>
		/// Gets the X coordinate
		/// </summary>
		[Pure]
		public T X
		{
			get
			{
				Contract.Ensures(Contract.Result<T>().Equals(_x));
				return _x;
			}
		}

		private readonly T _y;

		/// <summary>
		/// Gets the Y coordinate
		/// </summary>
		[Pure]
		public T Y
		{
			get
			{
				Contract.Ensures(Contract.Result<T>().Equals(_y));
				return _y;
			}
		}

		private readonly T _z;

		/// <summary>
		/// Gets the Z coordinate
		/// </summary>
		[Pure]
		public T Z
		{
			get
			{
				Contract.Ensures(Contract.Result<T>().Equals(_z));
				return _z;
			}
		}

		/// <summary>
		/// Determines whether the specified <see cref="System.Object" /> is equal to this instance.
		/// </summary>
		/// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
		/// <returns>
		///   <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
		/// </returns>
		[Pure]
		public override bool Equals(object obj)
		{
			if (ReferenceEquals(null, obj)) return false;
			return obj is Coordinates3<T> && Equals((Coordinates3<T>)obj);
		}

		/// <summary>
		/// Determines whether the specified coordinates are equal to this instance.
		/// </summary>
		/// <param name="other">The coordinates to compare with this instance.</param>
		/// <returns>
		///   <c>true</c> if the specified coordinates are equal to this instance; otherwise, <c>false</c>.
		/// </returns>
		[Pure]
		public bool Equals(Coordinates3<T> other)
		{
			Contract.Ensures(Contract.Result<bool>() == false || (_x.Equals(other._x) && _y.Equals(other._y) && _z.Equals(other._z)));
			return _x.Equals(other._x) && _y.Equals(other._y) && _z.Equals(other._z);
		}

		/// <summary>
		/// Returns a hash code for this instance.
		/// </summary>
		/// <returns>
		/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
		/// </returns>
		[Pure]
		public override int GetHashCode()
		{
			unchecked
			{
				int hashCode = _x.GetHashCode();
				hashCode = (hashCode * 397) ^ _y.GetHashCode();
				hashCode = (hashCode * 397) ^ _z.GetHashCode();
				return hashCode;
			}
		}

		public static bool operator ==(Coordinates3<T> left, Coordinates3<T> right)
		{
			return left.Equals(right);
		}

		public static bool operator !=(Coordinates3<T> left, Coordinates3<T> right)
		{
			return !left.Equals(right);
		}
	}
}